package com.cskaoyan.recursion;

/*
        1.有三根杆子A，B，C。A杆上有 N 个 (N>1) 穿孔圆盘，盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至 C 杆：
            a.每次只能移动一个圆盘；
            b.大盘不能叠在小盘上面。
            在汉诺塔问题中N 64 亿年
        提示：可将圆盘临时置于 B 杆，也可将从 A 杆移出的圆盘重新移回 A 杆，但都必须遵循上述两条规则。
        问：最少要移动多少次？如何移？

        递归算法的核心思想:
            在解决大规模问题的时候，可能因为问题的规模太大，无法直接解决这个问题，转而将大规模的问题，分解为一系列
            小规模的子问题，当规模小到一定程度的时候，我们可以直接解决小规模的子问题，我们只要解决这一系列小规模的子问题，
            就相当于解决了大规模的问题

            分而治之


    解决思路以N个圆盘为例：
    1. 当我们要解决N个圆盘的搬运问题，对于N个圆盘，我们可能无法一次性得到结果，
       于是，我们把N个圆盘的搬运问题， -> 最大一个的圆盘的搬运 & 最大圆盘上面的 n - 1

    2. 对于规模为1那个待搬运的最大的圆盘，直接就知道如何搬运（一步搞定）

    3. 在2的基础上，只需要，再解决 N - 1个圆盘搬运的问题

    三个盘：
    A--->C
    A--->B
    C--->B
    A--->C
    B--->A
    B--->C
    A--->C

    f(n)表示n个圆盘搬运的总次数
    f(n) = f(n - 1) + 1 + f(n - 2)
         = 2 * f(n - 1) + 1

    f(n) + 1 = 2 * f(n - 1) + 2 = 2 * (f(n-1) + 1)
    f(1) + 1 = 2

    f(n) + 1 = 2^n
    f(n) = 2^n - 1
 */
public class Demo2Hanoi {


    public static void main(String[] args) {
        int n = 4;
        hanoi(n, 'A', 'B', 'C');

        // 计算hanoi搬运的次数
        long count = countHanoi(n);
        System.out.println(count);
    }


    /*
            参数n: 表示带搬运的圆盘数量
            返回值: 表示n个圆盘搬运的总次数
     */
    public static long countHanoi(int n) {

        if (n == 1) {
            // 只有一个盘，一步就搬运完毕
            return 1;
        }

        return 2 * countHanoi(n - 1) + 1;
    }





    /*
            n：表示带待搬运的圆盘数量
            start: 这n个盘当前所在的杆的名字
            middle: 搬运过程中辅助杆的名字
            end: 这n个盘要移动到的目标杆的名字
     */
    public static void hanoi(long n, char start, char middle, char end) {
        // 定义递归出口
        if (n == 1) {
            System.out.println(start + "--->" + end );
            return;
        }

        // 将最大的盘上面n - 1盘，从start ——> middle, 以end为辅助
        hanoi(n - 1, start, end, middle);

        // 对于start杆上最大的那个盘 start —— end
        System.out.println(start + "--->" + end);

       // 将 middle杆上剩下的n-1 移动到 end杆
        hanoi(n - 1, middle, start, end);
    }




}
